package com.sudo.common.task;

import com.sudo.common.annotation.Note;
import com.sudo.common.component.SpringContextUtil;
import com.sudo.common.utils.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
public class TaskUtil {

    // 根据方法名 反射 验证参数类型是否匹配和必填项
    public static boolean verifyParam(String beanName,String methodName,Map<String, Object> paramMap) {

        try {
            // Class<?> aClass = Class.forName(stackTraceElement.getClassName());
            // String methodName = stackTraceElement.getMethodName();
            // aClass.getDeclaredMethod(methodName, paramMap.getClass()); // 这里不能这样写（LinkedHashMap），参数必须一致，
            Method declaredMethod = BaseTaskRegistry.getClassByBean(beanName).getDeclaredMethod(methodName, Map.class);
            Note annotation = declaredMethod.getAnnotation(Note.class);

            String[] params = annotation.params().split("\\|");
            for (String string : params) {
                TaskParam taskParam = new TaskParam(string);
                // 1.验证参数有无
                boolean hasKey = paramMap.containsKey(taskParam.getKey());
                if (taskParam.isRequire() && !hasKey){
                    // 如果是必填参数，但是没有，则验证不通过
                    log.error("必填参数{}不存在",taskParam.getKey());
                    return false;
                }
                // 2.验证参数类型是否合法
                Object paramValue = paramMap.get(taskParam.getKey());
                if (!verifyObjectType(paramValue, taskParam.getType())) {
                    log.error("参数：{}，类型{} 与值：{}，类型不匹配",taskParam.getKey(),taskParam.getType(),paramValue);
                    return false;
                }
            }

        } catch (NoSuchMethodException e) {
            log.error("定时任务参数验证异常：{0}",e);
            return false;
        }
        return true;
    }


    // 根据方法名 反射 验证参数类型是否匹配和必填项
    public static boolean verifyParam(String methodParams,Map<String, Object> paramMap) {

        String[] params = methodParams.split("\\|");
        for (String string : params) {
            TaskParam taskParam = new TaskParam(string);
            // 1.验证参数有无
            boolean hasKey = paramMap.containsKey(taskParam.getKey());
            if (taskParam.isRequire() && !hasKey){
                // 如果是必填参数，但是没有，则验证不通过
                log.error("必填参数{}不存在",taskParam.getKey());
                return false;
            }
            // 2.验证参数类型是否合法
            Object paramValue = paramMap.get(taskParam.getKey());
            if (!verifyObjectType(paramValue, taskParam.getType())) {
                log.error("参数：{}，类型{} 与值：{}，类型不匹配",taskParam.getKey(),taskParam.getType(),paramValue);
                return false;
            }
        }
        return true;
    }

    // 根据方法名获取方法文档
    public static TaskMethod getTaskMethodDocument(Class<?> clazz,String methodName, Integer paramCount) {
        TaskMethod taskMethod = new TaskMethod();
        try {
            Method declaredMethod = null;
            if (paramCount == 0){
                declaredMethod = clazz.getDeclaredMethod(methodName);
            }else {
                declaredMethod = clazz.getDeclaredMethod(methodName, Map.class);
            }
            Note annotation = declaredMethod.getAnnotation(Note.class);
            taskMethod.setMethodName(methodName);
            taskMethod.setMethodDesc(annotation.desc());
            List<TaskParam> taskParamList = new ArrayList<>();
            String[] params = annotation.params().split("\\|");
            for (String string : params) {
                if (StringUtils.isNotBlank(string)) {
                    TaskParam taskParam = new TaskParam(string);
                    taskParamList.add(taskParam);
                }
            }
            taskMethod.setTaskParamList(taskParamList);
        } catch (NoSuchMethodException e) {
            log.error("获取方法文档异常：{0}",e);
        }
        return taskMethod;
    }

    private static boolean verifyObjectType(Object object ,String type){
        switch (type){
            case "String":
                return object instanceof String;
            case "Integer":
                return object instanceof Integer;
            case "Boolean":
                return object instanceof Boolean;
            default:
                return false;
        }
    }

    public static void callTaskMethod(String beanName,String methodName,String params) throws Exception {
        Class<?> aClass = BaseTaskRegistry.getClassByBean(beanName);
        Object bean = SpringContextUtil.getBean(beanName);
        Method declaredMethod = null;
        if (StringUtils.isBlank(params)) {
            declaredMethod = aClass.getDeclaredMethod(methodName);
            declaredMethod.invoke(bean);
        }else {
            declaredMethod = aClass.getDeclaredMethod(methodName,Map.class);
            Map<String,Object> paramMap = JsonUtil.jsonToPojo(params, Map.class);
            declaredMethod.invoke(bean,paramMap);
        }
    }


    // 获取任务调度模块列表（每个模块对应的beanName）
    public static Map<String,String> getTaskModuleMap() {
        Map<String, String> stringMap = new HashMap<>();
        BaseTaskRegistry.getBeanClassMap().forEach((k, v)->{
            stringMap.put(k, StringUtils.uncapitalize(v.getSimpleName()));
        });
        return stringMap;
    }

    public static Map<String, Map<String,TaskMethod>> getTaskMethodParamCountMap(){
        Map<String, Map<String,TaskMethod>> stringMapMap = new HashMap<>();
        BaseTaskRegistry.getBeanClassMap().forEach((k, v)->{
            Map<String, TaskMethod> stringTaskMethodMap = new HashMap<>();
            Constructor<?> declaredConstructor = v.getDeclaredConstructors()[0];
            for (Method declaredMethod : v.getDeclaredMethods()) {
                if (!declaredConstructor.getName().equals(declaredMethod.getName())){
                    TaskMethod taskMethodDocument = getTaskMethodDocument(v,declaredMethod.getName(), declaredMethod.getParameterCount());
                    stringTaskMethodMap.put(declaredMethod.getName(), taskMethodDocument);
                }
            }
            stringMapMap.put(StringUtils.uncapitalize(v.getSimpleName()), stringTaskMethodMap);
        });
        return stringMapMap;
    }
}
